// Copyright 2019 Google LLC.
#ifndef ParagraphStyle_DEFINED
#define ParagraphStyle_DEFINED

#include "include/core/SkFontStyle.h"
#include "include/core/SkScalar.h"
#include "include/core/SkString.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/FontRastrSettings.h"
#include "modules/skparagraph/include/TextStyle.h"

#include <native_drawing/drawing_text_declaration.h>
#include <native_drawing/drawing_text_typography.h>
#include <stddef.h>
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTraceEventCommon.h"
namespace skia {
namespace textlayout {

struct StrutStyle {
    StrutStyle();

    const std::vector<SkString>& getFontFamilies() const { return fFontFamilies; }
    void setFontFamilies(std::vector<SkString> families) { fFontFamilies = std::move(families); }

    SkFontStyle getFontStyle() const { return fFontStyle; }
    void setFontStyle(SkFontStyle fontStyle) { fFontStyle = fontStyle; }

    SkScalar getFontSize() const { return fFontSize; }
    void setFontSize(SkScalar size) { fFontSize = size; }

    void setHeight(SkScalar height) { fHeight = height; }
    SkScalar getHeight() const { return fHeight; }

    void setLeading(SkScalar Leading) { fLeading = Leading; }
    SkScalar getLeading() const { return fLeading; }

    bool getStrutEnabled() const { return fEnabled; }
    void setStrutEnabled(bool v) { fEnabled = v; }

    bool getForceStrutHeight() const { return fForceHeight; }
    void setForceStrutHeight(bool v) { fForceHeight = v; }

    bool getHeightOverride() const { return fHeightOverride; }
    void setHeightOverride(bool v) { fHeightOverride = v; }

    void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; }
    bool getHalfLeading() const { return fHalfLeading; }

    bool operator==(const StrutStyle& rhs) const {
        return this->fEnabled == rhs.fEnabled &&
               this->fHeightOverride == rhs.fHeightOverride &&
               this->fForceHeight == rhs.fForceHeight &&
               this->fHalfLeading == rhs.fHalfLeading &&
               nearlyEqual(this->fLeading, rhs.fLeading) &&
               nearlyEqual(this->fHeight, rhs.fHeight) &&
               nearlyEqual(this->fFontSize, rhs.fFontSize) &&
               this->fFontStyle == rhs.fFontStyle &&
               this->fFontFamilies == rhs.fFontFamilies;
    }

private:

    std::vector<SkString> fFontFamilies;
    SkFontStyle fFontStyle;
    SkScalar fFontSize;
    SkScalar fHeight;
    SkScalar fLeading;
    bool fForceHeight;
    bool fEnabled;
    bool fHeightOverride;
    // true: half leading.
    // false: scale ascent/descent with fHeight.
    bool fHalfLeading;
};

struct TextIndent {
    TextIndent();

    bool operator==(const TextIndent& rhs) const {
        return this->fFirstLine == rhs.fFirstLine &&
               this->fRestLine == rhs.fRestLine;
    }

    SkScalar getFirstLine() const { return fFirstLine; }
    void setFirstLine(SkScalar firstLine) { fFirstLine = firstLine; }

    SkScalar getRestLine() const { return fRestLine; }
    void setRestLine(SkScalar restLine) { fRestLine = restLine; }

private:
    SkScalar fFirstLine;
    SkScalar fRestLine;
};

struct ParagraphStyle {
    ParagraphStyle();
    ~ParagraphStyle() {
        if (fStyleOHOS) {
            OH_Drawing_DestroyTypographyStyle(fStyleOHOS);
        }
    }
    
    ParagraphStyle(const ParagraphStyle& src) {
        TRACE_EVENT0("skia", "ParagraphStyle::Copy");
        CopyNormalStyle(src);
        this->fStyleOHOS = OH_Drawing_CreateTypographyStyle();
        fillOHOSStyle();
    }
    ParagraphStyle& operator=(const ParagraphStyle& src){
        TRACE_EVENT0("skia", "ParagraphStyle::Copy");
        CopyNormalStyle(src);
        fillOHOSStyle();
        return *this;
    }
    
    ParagraphStyle(ParagraphStyle&& src) {
        TRACE_EVENT0("skia", "ParagraphStyle::Move");
        this->fStyleOHOS = src.fStyleOHOS;
        src.fStyleOHOS = nullptr;
        MoveNormalStyle(std::move(src));
    }
    ParagraphStyle& operator=(ParagraphStyle&& src) {
        TRACE_EVENT0("skia", "ParagraphStyle::Move");
        if (fStyleOHOS) {
            OH_Drawing_DestroyTypographyStyle(fStyleOHOS);
        }
        fStyleOHOS = src.fStyleOHOS;
        src.fStyleOHOS = nullptr;
        MoveNormalStyle(std::move(src));
        return *this;
    }

    bool operator==(const ParagraphStyle& rhs) const {
//         return this->fHeight == rhs.fHeight &&
//                this->fEllipsis == rhs.fEllipsis &&
//                this->fEllipsisUtf16 == rhs.fEllipsisUtf16 &&
//                this->fTextDirection == rhs.fTextDirection && this->fTextAlign == rhs.fTextAlign &&
//                this->fDefaultTextStyle == rhs.fDefaultTextStyle &&
//                this->fTextIndent == rhs.fTextIndent &&
//                this->fReplaceTabCharacters == rhs.fReplaceTabCharacters;
        return OH_Drawing_TypographyStyleEquals(*this, rhs);
    }

    const StrutStyle& getStrutStyle() const { return fStrutStyle; }
    void setStrutStyle(StrutStyle strutStyle) {
        // ref: openharmony graphic_graphic_2d: rosen/modules/2d_graphics/drawing_ndk/src/drawing_text_typography.cpp
        // OH_Drawing_SetTypographyStyleTextStrutStyle and OH_Drawing_TypographyStyleGetStrutStyle
        
        fStrutStyle = std::move(strutStyle);

        // OHOS not support for fFontStyle.width, force to normal
        OH_Drawing_StrutStyle tmpStrutStyleOHOS;
        
        if (fStrutStyle.getFontFamilies().size() > 0) {
            fFontFamiliesOHOS.clear();
            for (const auto& str : fStrutStyle.getFontFamilies()) {
                fFontFamiliesOHOS.push_back(str.c_str());
            }
//            OH_Drawing_SetTypographyTextLineStyleFontFamilies(fStyleOHOS, fFontFamiliesOHOS.size(), fFontFamiliesOHOS.data());
        }

        tmpStrutStyleOHOS.familiesSize = fFontFamiliesOHOS.size();
        tmpStrutStyleOHOS.families = const_cast<char**>(reinterpret_cast<const char**>(fFontFamiliesOHOS.data()));
        tmpStrutStyleOHOS.weight = TextStyle::toOHFontWeight(fStrutStyle.getFontStyle());
        tmpStrutStyleOHOS.style = (OH_Drawing_FontStyle)fStrutStyle.getFontStyle().slant();
        tmpStrutStyleOHOS.size = fStrutStyle.getFontSize();
        tmpStrutStyleOHOS.heightScale = fStrutStyle.getHeight();
        tmpStrutStyleOHOS.halfLeading = fStrutStyle.getHalfLeading();
        tmpStrutStyleOHOS.leading = fStrutStyle.getLeading();
        tmpStrutStyleOHOS.forceStrutHeight = fStrutStyle.getForceStrutHeight();
        // NOTE: heightOverride will not working!
        // the func OH_Drawing_SetTypographyStyleTextStrutStyle does not use the heightOverride property at all!
        tmpStrutStyleOHOS.heightOverride = fStrutStyle.getHeightOverride();
        
        // NOTE: After OH_Drawing_SetTypographyTextLineStyleFontHeight in OH_Drawing_SetTypographyStyleTextStrutStyle,
        // the heightOverride of StrutStyle is force to true, and cannot set to false
        OH_Drawing_SetTypographyStyleTextStrutStyle(fStyleOHOS, &tmpStrutStyleOHOS);
        
        auto retStrutStyle = OH_Drawing_TypographyStyleGetStrutStyle(fStyleOHOS);
        if (retStrutStyle) {
            fStrutStyle.setHeightOverride(retStrutStyle->heightOverride);
        }

        // invoke this func to enable
        // ref: rosen/test/2d_graphics/unittest/ndk/drawing_text_typography_strut_style_test.cpp
        // HWTEST_F(OhDrawingTypographyStrutStyleTest, OhDrawingTypographyStrutStyleTest003, TestSize.Level1)
        OH_Drawing_SetTypographyTextUseLineStyle(fStyleOHOS, fStrutStyle.getStrutEnabled());
    }

    const FontRastrSettings& getFontRastrSettings() const { return fFontRastrSettings; }
    void setFontRastrSettings(FontRastrSettings fontRastrSettings) { fFontRastrSettings = fontRastrSettings; }

    const TextStyle& getTextStyle() const { return fDefaultTextStyle; }
    void setTextStyle(const TextStyle& textStyle) {
        fDefaultTextStyle = textStyle;
        OH_Drawing_SetTypographyTextStyle(fStyleOHOS, textStyle);
    }

    TextDirection getTextDirection() const { return fTextDirection; }
    void setTextDirection(TextDirection direction) {
        fTextDirection = direction;
        static_assert((int)TextDirection::kRtl == OH_Drawing_TextDirection::TEXT_DIRECTION_RTL, "TextDirection not equal!");
        static_assert((int)TextDirection::kLtr == OH_Drawing_TextDirection::TEXT_DIRECTION_LTR, "TextDirection not equal!");
        OH_Drawing_SetTypographyTextDirection(fStyleOHOS, (OH_Drawing_TextDirection)direction);
    }

    TextAlign getTextAlign() const { return fTextAlign; }
    void setTextAlign(TextAlign align) {
        fTextAlign = align;
        static_assert((int)TextAlign::kLeft == OH_Drawing_TextAlign::TEXT_ALIGN_LEFT, "TextAlign not equal!");
        static_assert((int)TextAlign::kRight == OH_Drawing_TextAlign::TEXT_ALIGN_RIGHT, "TextAlign not equal!");
        static_assert((int)TextAlign::kCenter == OH_Drawing_TextAlign::TEXT_ALIGN_CENTER, "TextAlign not equal!");
        static_assert((int)TextAlign::kJustify == OH_Drawing_TextAlign::TEXT_ALIGN_JUSTIFY, "TextAlign not equal!");
        static_assert((int)TextAlign::kStart == OH_Drawing_TextAlign::TEXT_ALIGN_START, "TextAlign not equal!");
        static_assert((int)TextAlign::kEnd == OH_Drawing_TextAlign::TEXT_ALIGN_END, "TextAlign not equal!");
        OH_Drawing_SetTypographyTextAlign(fStyleOHOS, (OH_Drawing_TextAlign)align);
    }

    size_t getMaxLines() const { return fLinesLimit; }
    void setMaxLines(size_t maxLines) {
        fLinesLimit = maxLines;
        OH_Drawing_SetTypographyTextMaxLines(fStyleOHOS, maxLines);
    }

    SkString getEllipsis() const { return fEllipsis; }
    std::u16string getEllipsisUtf16() const { return fEllipsisUtf16; }
    // skiko not use the u16string
    void setEllipsis(const std::u16string& ellipsis) {  fEllipsisUtf16 = ellipsis; }
    void setEllipsis(const SkString& ellipsis) {
        fEllipsis = ellipsis;
        OH_Drawing_SetTypographyTextEllipsis(fStyleOHOS, ellipsis.c_str());
    }

    SkScalar getHeight() const { return fHeight; }
    void setHeight(SkScalar height) {
        fHeight = height;
        OH_Drawing_SetTypographyTextFontHeight(fStyleOHOS, height);
    }

    TextHeightBehavior getTextHeightBehavior() const { return fTextHeightBehavior; }
    void setTextHeightBehavior(TextHeightBehavior v) {
        fTextHeightBehavior = v;
        static_assert((int)TextHeightBehavior::kAll == OH_Drawing_TextHeightBehavior::TEXT_HEIGHT_ALL, "TextHeightBehavior not equal!");
        static_assert((int)TextHeightBehavior::kDisableFirstAscent == OH_Drawing_TextHeightBehavior::TEXT_HEIGHT_DISABLE_FIRST_ASCENT, "TextHeightBehavior not equal!");
        static_assert((int)TextHeightBehavior::kDisableLastDescent == OH_Drawing_TextHeightBehavior::TEXT_HEIGHT_DISABLE_LAST_ASCENT, "TextHeightBehavior not equal!");
        static_assert((int)TextHeightBehavior::kDisableAll == OH_Drawing_TextHeightBehavior::TEXT_HEIGHT_DISABLE_ALL, "TextHeightBehavior not equal!");

        OH_Drawing_TypographyTextSetHeightBehavior(fStyleOHOS, (OH_Drawing_TextHeightBehavior)v);
    }

    bool unlimited_lines() const {
//         return fLinesLimit == std::numeric_limits<size_t>::max();
        return OH_Drawing_TypographyIsLineUnlimited(fStyleOHOS);
    }
    bool ellipsized() const {
//         return !fEllipsis.isEmpty() || !fEllipsisUtf16.empty();
        return OH_Drawing_TypographyIsEllipsized(fStyleOHOS);
    }
    TextAlign effective_align() const;
    bool hintingIsOn() const {
//         return fHintingIsOn;
        return OH_Drawing_TypographyStyleIsHintEnabled(fStyleOHOS);
    }
    void turnHintingOff() {
        fHintingIsOn = false;
        OH_Drawing_TypographyStyleSetHintsEnabled(fStyleOHOS, fHintingIsOn);
    }

    bool getReplaceTabCharacters() const { return fReplaceTabCharacters; }
    void setReplaceTabCharacters(bool value) { fReplaceTabCharacters = value; }

    const TextIndent& getTextIndent() const { return fTextIndent; }
    void setTextIndent(TextIndent textIndent) { fTextIndent = textIndent; }

    operator OH_Drawing_TypographyStyle*() const {
        return fStyleOHOS;
    }

private:
    void fillOHOSStyle();
    void CopyNormalStyle(const ParagraphStyle& src) {
        this->fStrutStyle = src.fStrutStyle;
        this->fDefaultTextStyle = src.fDefaultTextStyle;
        this->fTextAlign = src.fTextAlign;
        this->fTextDirection = src.fTextDirection;
        this->fLinesLimit = src.fLinesLimit;
        this->fEllipsisUtf16 = src.fEllipsisUtf16;
        this->fEllipsis = src.fEllipsis;
        this->fHeight = src.fHeight;
        this->fTextHeightBehavior = src.fTextHeightBehavior;
        this->fHintingIsOn = src.fHintingIsOn;
        this->fReplaceTabCharacters = src.fReplaceTabCharacters;
        this->fTextIndent = src.fTextIndent;
    }
    void MoveNormalStyle(ParagraphStyle&& src) {
        this->fStrutStyle = std::move(src.fStrutStyle);
        this->fDefaultTextStyle = std::move(src.fDefaultTextStyle);
        this->fTextAlign = std::move(src.fTextAlign);
        this->fTextDirection = std::move(src.fTextDirection);
        this->fLinesLimit = std::move(src.fLinesLimit);
        this->fEllipsisUtf16 = std::move(src.fEllipsisUtf16);
        this->fEllipsis = std::move(src.fEllipsis);
        this->fHeight = std::move(src.fHeight);
        this->fTextHeightBehavior = std::move(src.fTextHeightBehavior);
        this->fHintingIsOn = std::move(src.fHintingIsOn);
        this->fReplaceTabCharacters = std::move(src.fReplaceTabCharacters);
        this->fTextIndent = std::move(src.fTextIndent);
    }
    StrutStyle fStrutStyle;
    std::vector<const char*> fFontFamiliesOHOS;
    TextStyle fDefaultTextStyle;
    TextAlign fTextAlign;
    TextDirection fTextDirection;
    size_t fLinesLimit;
    std::u16string fEllipsisUtf16;
    SkString fEllipsis;
    SkScalar fHeight;
    TextHeightBehavior fTextHeightBehavior;
    bool fHintingIsOn;
    bool fReplaceTabCharacters;
    TextIndent fTextIndent;
    FontRastrSettings fFontRastrSettings;
    OH_Drawing_TypographyStyle *fStyleOHOS = nullptr;
};
}  // namespace textlayout
}  // namespace skia

#endif  // ParagraphStyle_DEFINED
